<!DOCTYPE html>
<html>
<head>
    <title>学习JavaScript</title>
</head>
<body>

    <!-- document -->
    <dl id="drinkMenu" style="border: 1px solid #ccc; padding: 6px;">
        <dt>摩卡</dt>
        <dd>热摩卡咖啡</dd>
        <dt>酸奶</dt>
        <dd>老北京酸奶</dd>
        <dt>果汁</dt>
        <dd>鲜榨苹果汁</dd>
    </dl>
    
    <!-- 操作 DOM -->
    <div id="testDiv">
        <div class="cRed">
            <p id="testP">
                Javascript
            </p>
            <p>
                Java
            </p>
        </div>
        <div class="cRed cGreen">
            <p>
                Python
            </p>
            <p>
                Ruby
            </p>
            <p>
                Swift
            </p>
        </div>
        <div class="cGreen">
            <p>
                Scheme
            </p>
            <p>
                Haskell
            </p>
        </div>
    </div>

    <!-- 更新 DOM -->
    <div id="testDiv01">
        <p id="testJs">
            javascript
        </p>
        <p>
            Java
        </p>
    </div>

    <!-- 插入 DOM -->
    <p id="js">
        JavaScript
    </p>
    <div id="list">
        <p id="java">
            Java
        </p>
        <p id="python">
            Python
        </p>
        <p id="scheme">
            Scheme
        </p>
    </div>

    <!-- insertBefore -->
    <div id="insertBefore">
        <p id="aaa">
            AAA
        </p>
        <p id="bbb">
            BBB
        </p>
        <p id="ccc">
            CCC
        </p>
    </div>

    <!-- 插入 DOM 练习 -->
    <ol id="testList">
        <li class="lang">Scheme</li>
        <li class="lang">JavaScript</li>
        <li class="lang">Python</li>
        <li class="lang">Ruby</li>
        <li class="lang">Haskell</li>
    </ol>

    <!-- 删除 DOM -->
    <div id="parent">
        <p>First</p>
        <p>Second</p>
    </div>

    <!-- 删除 DOM 练习 -->
    <ul id="testList01">
        <li>JavaScript</li>
        <li>Swift</li>
        <li>HTML</li>
        <li>ANSI C</li>
        <li>CSS</li>
        <li>DirectX</li>
    </ul>

    <!-- 提交表单 -->
    <form action="" id="testForm">
        <input type="text" name="test" />
        <button type="button" onclick="doSubmitForm()">
            Submit
        </button>
    </form>
    <form action="" id="testForm01" onsubmit="return checkForm()">
        <input type="text" name="test01" />
        <button type="submit">
            Submit
        </button>
    </form>
    <form action="" id="loginForm" method="post" onsubmit="return checkForm01()">
        <input type="text" id="username" name="username" />
        <input type="password" id="password" name="password" />
        <button type="submit">
            Submit
        </button>
    </form>
    <form action="" id="loginForm01" method="post" onsubmit="return checkForm02()">
        <input type="text" id="username01" name="username01" />
        <input type="password" id="inputPassword" />
        <input type="hidden" id="md5Password" name="password01" />
        <button type="submit">
            Submit
        </button>
    </form>
    <!-- 获取 单选框 radio 和 复选框 checkbox 的值 -->
    <div>
        <label for="monday">
            星期一：
            <input type="radio" name="weekday" id="monday" value="1" checked />
        </label>
        <label for="tuesday">
            星期二：
            <input type="radio" name="weekday" id="tuesday" value="2" />
        </label>
    </div>
    <div>
        <label for="basketball">
            篮球
            <input type="checkbox" name="hobby" id="basketball" value="01" />
        </label>
        <label for="badminton">
            羽毛球
            <input type="checkbox" name="hobby" id="badminton" value="02" />
        </label>
        <label for="draw">
            画画
            <input type="checkbox" name="hobby" id="draw" value="03" checked="checked" />
        </label>
    </div>
    <!-- 提交表单 练习 -->
    <form action="#" id="testRegister" target="_blank" onsubmit="return checkRegisterForm()">
        <p id="testError" style="color: red;"></p>
        <label for="crfName">
            用户名：<input type="text" id="crfName" name="crfusername" />
        </label>
        <label for="crfPwd">
            口令：<input type="password" id="crfPwd" name="crfpassword" />
        </label>
        <label for="crfPwdRe">
            重复口令：<input type="password" id="crfPwdRe" />
        </label>
        <button type="submit">
            提交
        </button>
        <button type="reset">
            重置
        </button>
    </form>

    <!-- 操作文件 -->
    <!--<script>
        $(function(){
            var fileInput = document.getElementById('testFileUpload'),
                filePath = document.getElementById('testGetFilename');
            fileInput.addEventListener('change', function(){
                filePath.innerText = fileInput.value;
            });
        });
    </script>-->
    <form action="http://localhost/test" method="post" enctype="multipart/form-data">
        <input type="file" id="testFileUpload" name="test" />
        <p>
            待上传文件：<span id="testGetFilename" style="color: red;"></span>
        </p>
    </form>

    <!-- File API -->
    <form method="" action="" enctype="multipart/form-data">
        <p>图片预览：</p>
        <div id="test-image-preview" style="width: 50%; height: 200px; border: 1px solid #ccc; background-size: contain; background-repeat: no-repeat; background-position: center center;">
        </div>
        <input type="file" id="test-image-file" name="test" />
        <p id="test-file-info"></p>
    </form>

    <script type="text/javascript">
        // 浏览器对象
        // JavaScript 可以获取浏览器提供的很多对象，并进行操作。

        // window
        // window 对象 不但充当 全局作用域，而且表示 浏览器窗口。
        // window 对象 有 innerWidth 和 innerHeight 属性，可以获取浏览器窗口的内部宽度和高度。内部宽高是指除去菜单栏、工具栏、边框等占位元素后，用于显示网页的净宽高。
        // 兼容性：IE<=8 不支持。
        // 可以调整浏览器窗口大小试试：
        console.log('window inner size: ' + window.innerWidth + ' x ' + window.innerHeight);   // window inner size: 2560 x 1297
        // outerWidth 和 outerHeight 属性，可以获取浏览器窗口的整个宽高。
        console.log(window.outerWidth + ' x ' + window.outerHeight);   // 2560 x 1400

        // navigator
        // navigator 对象表示浏览器的信息，最常用的属性包括：
        // navigator.appName : 浏览器名称
        // navigator.appVersion : 浏览器版本
        // navigator.language : 浏览器设置的语言
        // navigator.platform : 操作系统类型
        // navigator.userAgent : 浏览器设定的 User-Agent 字符串
        //console.log('appName = ' + navigator.appName);
        //console.log('appVersion = ' + navigator.appVersion);
        //console.log('language = ' + navigator.language);
        //console.log('platform = ' + navigator.platform);
        //console.log('userAgent = ' + navigator.userAgent);
        // 请注意，navigator 的信息可以很容易地被用户修改，所以JavaScript读取的值不一定是正确的。
        // 充分利用JavaScript对不存在属性返回undefined的特性，直接用短路运算符||计算：
        var width = window.innerWidth || document.body.clientWidth;

        // screen
        // screen 对象表示屏幕的信息，常用的属性有：
        // screen.width: 屏幕宽度，以像素为单位；
        // screen.height: 屏幕高度，以像素为单位；
        // screen.colorDepth: 返回颜色位数，如8、16、24.
        console.log('Screen size = ' + screen.width + ' x ' + screen.height);

        // location
        // location 对象表示当前页面的URL信息。例如，一个完整的URL: http://www.example.com:8080/path/index.html?a=1&b=2#TOP
        // 可以用 location.href 获取。要获得URL各个部分的值，可以这么写：
        // location.protocol :   // 'http'
        // location.host :   // 'www.example.com'
        // location.port :   // '8080'
        // location.pathname :   // '/path/index.html'
        // location.search :   //  '?a=1&b=2'
        // location.hash :   // 'TOP'
        // 要加载一个新页面，可以调用 location.assign() 。
        // 如果要重新加载当前页面，调用 loaction.reload() 方法。
        /*if(confirm('重新加载当前页' + location.href + ' ?')){
            location.reload();
        }else{
            location.assign('/');   // 设置一个新的URL地址
        }*/
        console.log(location.href);
        console.log(location.protocol);

        // document
        // document 对象表示当前页面。由于HTML在浏览器中以 DOM 形式表示为树形结构，document 对象就是整个 DOM 树的根节点。
        // document 的 title 属性是从 HTML 文档中的 <title>xxx</title> 读取的，但是可以动态改变：
        //document.title = '努力学习JavaScript！';   // 请观察浏览器窗口标题的变化。
        // 要查找 DOM 树的某个节点，需要从 document 对象开始查找。最常用的查找是根据 ID 和 Tag Name。
        // 用 document 对象提供的 getElementById() 和 getElementByTagName() 可以按 ID 获得一个 DOM 节点 和 按 Tag 名称获得一组 DOM 节点：
        var menu = document.getElementById('dringMenu');
        var drinks = document.getElementsByTagName('dt');
        var dmI, dmS;
        dmS = '提供的饮料有：';
        for(dmI = 0; dmI < drinks.length; dmI++){
            dmS = dmS + drinks[dmI].innerHTML + ',';
        }
        console.log(dmS);
        // document 对象还有一个 cookie 属性，可以获取当前页面的 Cookie。
        // Cookie 是由服务器发送的 key-value 标示符。因为 HTTP 协议是无状态的，但是服务器要区分到底是哪个用户发过来的请求，就可以用 Cookies 来区分。
        // 当一个用户成功登录后，服务器发送一个 Cookie 给浏览器，例如user=ABC123XYZ(加密的字符串)...，此后，浏览器访问该网站时，会在请求头附上这个 Cookie，服务器根据 Cookie 即可区分出用户。
        // Cookie 还可以存储网站的一些设置，例如，页面显示的语言等等。
        // JavaScript 可以通过 document.cookie 读取当前页面的 Cookie :
        //console.log(document.cookie);
        // 如果引入的第三方的JavaScript中存在恶意代码，则www.foo.com网站将直接获取到www.example.com网站的用户登录信息。为了解决这个问题，服务器在设置Cookie时可以使用httpOnly

        // history
        // history对象保存了浏览器的历史记录，JavaScript可以调用 history 对象的 back() 或 forward ()，相当于用户点击了浏览器的“后退”或“前进”按钮。
        // 这个对象属于历史遗留对象，对于现代Web页面来说，由于大量使用AJAX和页面交互，简单粗暴地调用history.back()可能会让用户感到非常愤怒。
        // 在登录页登录成功时调用history.back()，试图回到登录前的页面。这是一种错误的方法。
        // 任何情况，你都不应该使用history这个对象了。


        // 操作 DOM
        // 由于 HTML 文档被 浏览器 解析后 就是一颗 DOM 树，要改变 HTML 的结构，就需要通过 JavaScript 来操作 DOM。
        // 始终记住 DOM 是一个树形结构。操作一个 DOM 节点 实际上就是这么几个操作：
        // 更新：更新该 DOM 节点 的内容，相当于更新了该 DOM 节点 表示的 HTML 的内容；
        // 遍历：遍历该 DOM 节点 下的子节点，以便进行进一步操作；
        // 添加：在该 DOM 节点 下新增一个子节点，相当于动态增加了一个 HTML 节点；
        // 删除：将该节点从 HTML 中删除，相当于删除了该 DOM 节点的内容 以及 它包含的所有子节点。
        // 在操作一个 DOM 节点前，我们需要通过各种方式先拿到这个 DOM 节点。最常用的方法是 document.getElmentById() 和 document.getElementsByTagName()，以及CSS选择器 document.getElementsByClassName()。
        // 由于 ID 在 HTML 文档中是唯一的，所以 document.getElmentById() 可以直接定位唯一的一个 DOM 节点。document.getElementsByTagName() 和 document.getElementsByClassName() 总是返回一组 DOM 节点。要精确地选择DOM，可以先定位父节点，再从父节点开始选择，以缩小范围。
        // 例如：
        // 返回ID为'test'的节点：
        //var test = document.getElementById('test');
        // 先定位ID为'test-table'的节点，再返回其内部所有tr节点：
        //var trs = document.getElementById('test-table').getElementsByTagName('tr');
        // 先定位ID为'test-div'的节点，再返回其内部所有class包含red的节点：
        //var reds = document.getElementById('test-div').getElementsByClassName('red');
        // 获取节点test下的所有直属子节点:
        //var cs = test.children;
        // 获取节点test下第一个、最后一个子节点：
        //var first = test.firstElementChild;
        //var last = test.lastElementChild;
        // 第二种方法是使用 querySelector() 和 querySelectorAll()，需要了解selector语法，然后使用条件来获取节点，更加方便：
        // 通过 querySelector 获取ID为q1的节点：
        //var q1 = document.querySelector('#q1');
        // 通过querySelectorAll获取q1节点内的符合条件的所有节点：
        //var ps = q1.querySelectorAll('div.highlighted > p');
        // 严格地讲，我们这里的DOM节点是指Element，但是 DOM 节点实际上是 Node，
        // 在 HTML 中，Node 包括 Element、Comment、CDATA_SECTION 等很多种，以及根节点 Document 类型，但是，绝大多数时候我们只关心Element，也就是实际控制页面结构的 Node，其他类型的 Node 忽略即可。根节点 Document 已经自动绑定为 全局变量document。
        // 练习
        // 请选择出指定条件的节点：
        // 选择<p>JavaScript</p>:
        var js = document.getElementById('testP');
        console.log(js);
        // 选择<p>Python</p>,<p>Ruby</p>,<p>Swift</p>:
        //var cdp = document.getElementsByClassName('cRed cGreen')[0].children;
        var cdp = document.getElementsByClassName('cRed cGreen')[0].getElementsByTagName('p');
        console.log(cdp);
        // 选择<p>Haskell</p>:
        var haskell = document.getElementsByClassName('cGreen')[1].lastElementChild;
        console.log(haskell);


        // 更新 DOM
        // 对 DOM 节点进行更新，可以直接修改节点的文本，方法有两种：
        // 一种是修改 innerHTML 属性，这个方式非常强大，不但可以修改一个 DOM 节点的文本内容，还可以直接通过 HTML 片段修改 DOM 节点内部的子树
        // element.innerHTML
        // 获取<p id="p-id">...</p>
        //var p = document.getElementById('p-id');
        // 设置文本为abc:
        //p.innerHTML = 'ABC'; // <p id="p-id">ABC</p>
        // 设置HTML:
        //p.innerHTML = 'ABC <span style="color:red">RED</span> XYZ';
        // <p>...</p>的内部结构已修改
        // 用 innerHTML 时要注意，是否需要写入HTML。如果写入的字符串是通过网络拿到了，要注意对字符编码来避免XSS攻击。
        // 第二种是修改 innerText 或 textContent 属性，这样可以自动对字符串进行 HTML 编码，保证无法设置任何 HTML 标签：
        // 获取 <p id="p-id">...</p>
        //var p = document.getElementById('p-id');
        // 设置文本
        // HTML 被自动编码，无法设置一个 <script>节点:
        // <p id="p-id">&lt;script&gt;alert("Hi")&lt;/script&gt;</p>
        // 两者的区别在于读取属性时，innerText 不返回隐藏元素的文本，而 textContent 返回所有文本。另外注意IE<9不支持textContent。
        // 修改 CSS 也是经常需要的操作。DOM 节点的 style 属性对应所有的 CSS，可以直接获取或设置。因为 CSS 允许 font-size 这样的名称，但它并非JavaScript有效的属性名，所以需要在JavaScript中改写为驼峰式命名fontSize：
        // 获取<p id="p-id">...</p>
        //var p = document.getElementById('p-id');
        // 设置CSS:
        //p.style.color = '#ff0000';
        //p.style.fontSize = '20px';
        //p.style.paddingTop = '2em';
        // 练习
        // 获取<p>javascript</p>节点:
        var testJs = document.getElementById('testJs');
        // 修改文本为JavaScript:
        testJs.innerHTML = 'JavaScript';
        // 修改CSS为: color: #ff0000, font-weight: bold
        testJs.style.color = '#ff0000';
        testJs.style.fontWeight = 'bold';


        // 插入 DOM
        // 当我们获得了某个 DOM 节点，想在这个 DOM 节点内插入新的 DOM，应该如何做？
        // 如果这个 DOM 节点是空的，例如，<div></div>，那么，直接使用innerHTML = '<span>child</span>'就可以修改DOM节点的内容，相当于“插入”了新的DOM节点。
        // 如果这个 DOM 节点不是空的，那就不能这么做，因为 innerHTML 会直接替换掉原来的所有子节点。有两个办法可以插入新的节点。
        // 一个是使用 appendChild，把一个子节点添加到父节点的最后一个子节点。例如：
        // 把<p id="js">JavaScript</p>添加到<div id="list">的最后一项：
        var js01 = document.getElementById('js'),
            list01 = document.getElementById('list');
        list01.appendChild(js01);
        // 因为我们插入的js节点已经存在于当前的文档树，因此这个节点首先会从原先的位置删除，再插入到新的位置。
        // 更多的时候我们会从零创建一个新的节点，然后插入到指定位置：
        var haskell01 = document.createElement('p');
        haskell01.id = 'haskell';
        haskell01.innerText = 'Haskell';
        list01.appendChild(haskell01);
        // 这样就动态添加了一个新的节点
        // 动态创建一个节点然后添加到 DOM 树中，可以实现很多功能。
        // 举个例子，下面的代码动态创建了一个<style>节点，然后把它添加到<head>节点的末尾，这样就动态地给文档添加了新的CSS定义：
        //var d = document.createElement('style');
        //d.setAttribute('type', 'text/css');
        //d.innerHTML = 'p { color: red; }';
        //document.getElementsByTagName('head')[0].appendChild(d);

        // insertBefore
        // parentElement.insertBefore(newElement, referenceElement); 把子节点插入到指定的位置，子节点会插入到 referenceElement 之前。
        // 把 DDD 插入到 BBB 之前：
        var insertBefore = document.getElementById('insertBefore'),
            bbb = document.getElementById('bbb'),
            ddd = document.createElement('p');
        ddd.id = 'ddd';
        ddd.innerText = 'DDD';
        insertBefore.insertBefore(ddd, bbb);
        // 可见，使用 insertBefore 重点是要拿到一个“参考子节点”的引用。很多时候，需要循环一个父节点的所有子节点，可以通过迭代 children 属性实现：
        var ibI, ibC,
            ibList = document.getElementById('insertBefore');
        for(ibI = 0; ibI < ibList.children.length; ibI++){
            ibC = ibList.children[ibI];   // 拿到第 i 个子节点
        }
        console.log(ibC);   // CCC
        // 练习：对于一个已有的HTML结构，按字符串顺序重新排序DOM节点：
        var testList = document.getElementById('testList');
        var pLi = document.getElementsByClassName('lang');
        console.log(pLi);
        for(var i17 = 0; i17 < pLi.length; i17++){
            for(var j03 = 0; j03 < pLi.length - 1; j03++){
                if(pLi[j03].innerText > pLi[j03 + 1].innerText){
                    testList.insertBefore(pLi[j03 + 1], pLi[j03]);
                }
            }
        }
        console.log(pLi[0].innerText);

        // 删除 DOM
        // 要删除一个节点，首先要获得该节点本身以及它的父节点，然后，调用父节点的 removeChild 把自己删掉
        // 拿到待删除节点
        //var self = document.getElementById('to-be-removed');
        // 拿到父节点
        //var parent = self.parentElement;
        // 删除
        //var removed = parent.removeChild(self);
        //removed === self;   // true
        // 注意到删除后的节点虽然不在文档树中了，但其实它还在内存中，可以随时再次被添加到别的位置。
        // 当你遍历一个父节点的子节点并进行删除操作时，要注意，children 属性是一个只读属性，并且它在子节点变化时会实时更新。
        var parent = document.getElementById('parent');
        parent.removeChild(parent.children[0]);
        //parent.removeChild(parent.children[1]);   // index.html:3234 Uncaught TypeError: Failed to execute 'removeChild' on 'Node': parameter 1 is not of type 'Node'.
        // 浏览器报错：parent.children[1] 不是一个有效的节点。原因就在于，当 <p>First</p> 节点被删除后，parent.children 的节点数量已经从 2 变为了 1，索引 [1] 已经不存在了。
        // 因此，删除多个节点时，要注意 children 属性时刻都在变化。
        var web = ['JavaScript', 'HTML', 'CSS'];
        var ul = document.getElementById('testList01');
        for(var li of ul.children){
            if(web.indexOf(li.innerText) === -1){
                ul.removeChild(li);
            }
        }


        // 操作表单
        // 获取值
        // 如果我们获得了一个 <input> 节点的引用，就可以直接调用 value 获得对应的用户输入值
        // <input type="text" id="email">
        // var input = document.getElementById('email');
        // input.value; // '用户输入的值'
        // 这种方式可以应用于text、password、hidden以及select。
        // 但是，对于单选框和复选框，value属性返回的永远是HTML预设的值，
        // 而我们需要获得的实际是用户是否“勾上了”选项，所以应该用checked判断：
        // <label><input type="radio" name="weekday" id="monday" value="1"> Monday</label>
        // <label><input type="radio" name="weekday" id="tuesday" value="2"> Tuesday</label>
        // var mon = document.getElementById('monday');
        // var tue = document.getElementById('tuesday');
        // mon.value; // '1'
        // tue.value; // '2'
        // mon.checked; // true或者false
        // tue.checked; // true或者false
        // 设置值
        // <input type="text" id="email">
        // var input = document.getElementById('email');
        // input.value = 'test@example.com'; // 文本框的内容已更新
        // 对于单选框和复选框，设置checked为true或false即可。

        // HTML5 控件
        // HTML5新增了大量标准控件，常用的包括date、datetime、datetime-local、color等，它们都使用<input>标签
        // <input type="date" value="2015-07-01">
        // <input type="datetime-local" value="2015-07-01T02:03:04">
        // <input type="color" value="#ff0000">
        // 提交表单
        function doSubmitForm(){
            var form = document.getElementById('testForm');
            // 可以在此修改form的input...
            // 提交form:
            form.submit();
        }
        // 这种方式的缺点是扰乱了浏览器对 form 的正常提交。
        // 浏览器默认点击按钮 <button type="submit"> 时提交表单，或者用户在最后一个输入框按回车键。
        // 因此，第二种方式是响应 <form> 本身的 onsubmit 事件，在提交 form 时做修改
        function checkForm(){
            var form01 = document.getElementById('testForm01');
            // 可以在此修改form的input...
            // 继续下一步:
            return true;
        }
        // 注意要 return true; 来告诉浏览器继续提交，
        // 如果 return false; ，浏览器将不会继续提交form，这种情况通常对应用户输入有误，提示用户错误信息后终止提交form。
        // 在检查和修改 <input> 时，要充分利用 <input type='hidden'> 来传递数据。
        // 例如，很多登录表单希望用户输入用户名和口令，但是，安全考虑，提交表单时不传输明文口令，而是口令的 MD5 。普通JavaScript开发人员会直接修改<input>：
        function checkForm01(){
            var pwd = document.getElementById('password');
            // 把用户输入的明文变为 MD5 ：
            pwd.value = toMD5(pwd.value);
            // 继续下一步：
            return true;
        }
        // 这个做法看上去没啥问题，但用户输入了口令提交时，口令框的显示会突然从几个*变成32个*（因为MD5有32个字符）。
        // 要想不改变用户的输入，可以利用 <input type="hidden"> 实现：
        function checkForm02(){
            var inputPwd = document.getElementById('inputPassword');
            var md5Pwd = document.getElementById('md5Password');
            // 把用户输入的明文变为 MD5 ：
            md5Pwd.value = toMD5(inputPwd);
            // 继续下一步：
            return true;
        }
        // 注意到 i d为 md5Password 的 <input> 标记了 name="password"，
        // 而用户输入的 id 为 inputPassword 的 <input> 没有 name 属性。没有 name 属性的 <input> 的数据不会被提交。
        var mon = document.getElementById('monday');
        var tue = document.getElementById('tuesday');
        var hb01 = document.getElementById('basketball');
        var hb02 = document.getElementById('badminton');
        var hb03 = document.getElementById('draw');
        console.log( mon.value + ' ' + mon.checked );   //
        console.log( tue.value + ' ' + tue.checked );   //
        console.log( hb01.value + ' ' + hb01.checked );   //
        console.log( hb02.value + ' ' + hb02.checked );   //
        console.log( hb03.value + ' ' + hb03.checked );   //
        // 练习
        // 利用 JavaScript 检查用户注册信息是否正确，在以下情况不满足时报错并阻止提交表单：
        // 用户名必须是3-10位英文字母或数字；
        // 口令必须是6-20位；
        // 两次输入口令必须一致。
        var checkRegisterForm = function(){
            var userName = document.getElementById('crfName');
            var userPwd = document.getElementById('crfPwd');
            var userPwdRe = document.getElementById('crfPwdRe');
            var nameReg = /^[_0-9a-zA-Z]{3,10}$/;
            //var nameReg = /^\w{3,10}$/;
            var crfError = document.getElementById('testError');
            /*if(!nameReg.test(userName.value)){
                crfError.innerHTML = '用户名必须是3-10位英文字母或数字！';
                return false;
            }
            if(userPwd.value.length < 6 || userPwd.value.length > 20){
                crfError.innerHTML = '口令必须是6-20位';
                return false;
            }
            if(userPwd.value !== userPwdRe.value){
                crfError.innerHTML = '两次输入口令不一致,请出现输入！';
                return false;
            }
            if(nameReg.test(userName.value) === true && userPwd.value.length >= 6 && userPwd.value.length <= 20 && userPwd.value === userPwdRe.value){
                return true;
            }else{
                return false;
            }*/
            if(nameReg.test(userName.value) === true && userPwd.value.length >= 6 && userPwd.value.length <= 20 && userPwd.value === userPwdRe.value){
                return true;
            }else if(!nameReg.test(userName.value)){
                //crfError.innerText = '用户名必须是3-10位英文字母或数字';
                crfError.innerHTML = '用户名必须是3-10位英文字母或数字！';
                return false;
            }else if(userPwd.value.length < 6 || userPwd.value.length > 20){
                crfError.innerHTML = '口令必须是6-20位';
                return false;
            }else if(userPwd.value !== userPwdRe.value){
                crfError.innerHTML = '两次输入口令不一致,请出现输入！';
                return false;
            }else{
                return false;
            }
        };


        // 操作文件
        // 在HTML表单中，可以上传文件的唯一控件就是<input type="file">。
        // 注意：当一个表单包含 <input type="file"> 时，表单的 enctype 必须指定为 multipart/form-data，method 必须指定为 post，
        // 浏览器才能正确编码并以 multipart/form-data 格式发送表单的数据。
        // 出于安全考虑，浏览器只允许用户点击 <input type="file"> 来选择本地文件，
        // 用 JavaScript 对 <input type="file"> 的 value 赋值是没有任何效果的。
        // 当用户选择了上传某个文件后，JavaScript 也无法获得该文件的真实路径：
        // 通常，上传的文件都由后台服务器处理，JavaScript 可以在提交表单时对文件扩展名做检查，以便防止用户上传无效格式的文件：
        var testFileUpload = document.getElementById('testFileUpload');
        //var testFileName = testFileUpload.value;   // 文件路径
        var filePathSpan = document.getElementById('testGetFilename');
        /*if(!testFileName || !(testFileName.endsWith('.jpg') || testFileName.endsWith('.png') || testFileName.endsWith('.gif'))){
            console.log('Can only upload image file.');
            //return false;
        }*/
        testFileUpload.addEventListener('onchange', function(){
            console.log(filePathSpan.innerText);
            console.log(testFileName);
            filePathSpan.innerText = testFileUpload.value;
        });


        // File API
        // HTML5 的 File API 提供了 File 和 FileReader 两个主要对象，可以获得文件信息并读取文件。
        // 如何读取用户选取的图片文件，并在一个<div>中预览图像：
        /*var fileInput01 = document.getElementById('test-image-file');
        var preview01 =document.getElementById('test-image-preview');
        var fileInfo01 = document.getElementById('test-file-info');
        fileInput01.addEventListener('change', function(){
            preview01.style.backgroundImage = '';
            if(!fileInput01.value){
                fileInfo01.innerHTML = '没有选择文件';
                return;
            }
            var file = fileInfo01.files[0];
            fileInfo01.innerHTML = '文件：' + file.name + '<br />' + 
                                '大小：' + file.size + '<br />' + 
                                '修改：' + file.lastModifiedDate;
            if(file.type !== 'image/jpeg' && file.type !== 'image/png' && file.type !== 'image/gif'){
                console.log('不是有效的图片文件！');
                return;
            }
            var reader01 = new FileReader();
            reader01.onload = function(e){
                var data01 = e.target.result,
                    index01 = data01.indexOf(';base64,');
                preview01.style.backgroundImage = 'url(' + data01 + ')';
            };
            reader01.readAsDataURL(file);
        });*/
        /*var fileInput = document.getElementById('test-image-file'),
            info = document.getElementById('test-file-info'),
            preview = document.getElementById('test-image-preview');
        // 监听change事件:
        fileInput.addEventListener('change', function () {
            // 清除背景图片:
            preview.style.backgroundImage = '';
            // 检查文件是否选择:
            if (!fileInput.value) {
                info.innerHTML = '没有选择文件';
                return;
            }
            // 获取File引用:
            var file = fileInput.files[0];
            // 获取File信息:
            info.innerHTML = '文件: ' + file.name + '<br>' +
                            '大小: ' + file.size + '<br>' +
                            '修改: ' + file.lastModifiedDate;
            if (file.type !== 'image/jpeg' && file.type !== 'image/png' && file.type !== 'image/gif') {
                alert('不是有效的图片文件!');
                return;
            }
            // 读取文件:
            var reader = new FileReader();
            reader.onload = function(e) {
                var
                    data = e.target.result; // '...(base64编码)...'            
                preview.style.backgroundImage = 'url(' + data + ')';
            };
            // 以DataURL的形式读取文件:
            reader.readAsDataURL(file);
        });*/
        $(function() {
            var fileInput = document.getElementById('test-image-file');
            var info = document.getElementById('test-file-info');
            var preview = document.getElementById('test-image-preview');
            fileInput.addEventListener('change', function () {
                preview.style.backgroundImage = '';
                if (!fileInput.value) {
                    info.innerHTML = '没有选择文件';
                    return;
                }
                var file = fileInput.files[0];
                info.innerHTML = '文件: ' + file.name + '<br>' +
                                '大小: ' + file.size + '<br>' +
                                '修改: ' + file.lastModifiedDate;
                if (file.type !== 'image/jpeg' && file.type !== 'image/png' && file.type !== 'image/gif') {
                    alert('不是有效的图片文件!');
                    return;
                }
                var reader = new FileReader();
                reader.onload = function(e) {
                    var
                        data = e.target.result;
                        index = data.indexOf(';base64,');
                    preview.style.backgroundImage = 'url(' + data + ')';
                };
                reader.readAsDataURL(file);
            });
        });


        // AJAX
        // AJAX 请求是异步执行的，也就是说，要通过 回调函数 获得响应。
        // 在现代浏览器上写 AJAX 主要依靠 XMLHttpRequest 对象：
        /*function success(text){
            var textarea = document.getElementById('test-response-text');
            textarea.value = text;
        }
        function fail(code){
            var textarea = document.getElementById('test-response-text');
            textarea.value = 'Error code: ' + code;
        }
        var request = new XMLHttpRequest();   // 新建 XMLHttpRequest 对象
        request.onreadystatechange = function(){   // 状态发生变化时，函数被回调
            if(request.readyState === 4){    // 成功完成
                // 判断响应结果：
                if(request.status === 200){
                    // 成功，通过 responseText 拿到响应的文本：
                    return success(request.responseText);
                }else{
                    // 失败，根据响应码判断失败原因：
                    return fail(request.status);
                }
            }else{
                // HTTP请求还在继续...
            }
        };
        // 发送请求：
        request.open('GET', '/api/categories');
        request.send();
        alert('请求已发送，请等等响应...');*/
        // 通过检测 window 对象是否有 XMLHttpRequest 属性来确定浏览器是否支持标准的XMLHttpRequest。注意，不要根据浏览器的navigator.userAgent来检测浏览器是否支持某个JavaScript特性，一是因为这个字符串本身可以伪造，二是通过IE版本判断JavaScript特性将非常复杂。
        // 当创建了 XMLHttpRequest 对象后，要先设置 onreadystatechange 的回调函数。在回调函数中，通常我们只需通过 readyState === 4 判断请求是否完成，如果已完成，再根据 status === 200 判断是否是一个成功的响应。
        // XMLHttpRequest 对象的 open() 方法有3个参数，第一个参数指定是 GET 还是 POST，第二个参数指定 URL 地址，第三个参数指定是否使用异步，默认是true，所以不用写。
        // 注意，千万不要把第三个参数指定为 false，否则浏览器将停止响应，直到 AJAX 请求完成。如果这个请求耗时10秒，那么10秒内你会发现浏览器处于“假死”状态。
        // 最后调用 send() 方法才真正发送请求。GET 请求不需要参数，POST 请求需要把 body 部分以字符串或者 FormData 对象传进去。

        // 安全限制
        // 上面代码的URL使用的是相对路径。如果你把它改为'http://www.sina.com.cn/'，再运行，肯定报错。在Chrome的控制台里，还可以看到错误信息。
        // 这是因为浏览器的同源策略导致的。默认情况下，JavaScript在发送AJAX请求时，URL的域名必须和当前页面完全一致。
        // 完全一致的意思是，域名要相同（www.example.com和example.com不同），协议要相同（http和https不同），端口号要相同（默认是:80端口，它和:8080就不同）。有的浏览器口子松一点，允许端口不同，大多数浏览器都会严格遵守这个限制。


        // Promise
        // 在 JavaScript 的世界中，所有代码都是单线程执行的。
        // 由于这个“缺陷”，导致 JavaScript 的所有网络操作，浏览器事件，都必须是异步执行。异步执行可以用回调函数实现：
        function callback01(){
            console.log('Done');
        }
        console.log('before setTimeout()');
        setTimeout(callback01, 1000);   // 1 秒钟后调用 callback01 函数
        console.log('after setTimeout()');
        // 观察上述代码执行，在 Chrome 的控制台输出可以看到：
        // before setTimeout()
        // after setTimeout()
        // 等待 1 秒后
        // Done

        // 可见，异步操作会在将来的某个时间点触发一个函数调用。

    </script>
     

</body>
</html>